package middleware

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gtime"
	"time"
)

// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims
// jwt包自带的jwt.StandardClaims只包含了官方字段
// 我们这里需要额外记录一个username字段，所以要自定义结构体
// 如果想要保存更多信息，都可以添加到这个结构体中
type MyUser struct {
	Id       int    `json:"id"         description:"用户ID"`
	Username string `json:"username"       description:"用户名称"`
}

type MyClaims struct {
	User *MyUser
	jwt.StandardClaims
}

const TokenExpireDuration = time.Hour * 144

//秘钥   加密算法中的加糖
var MySecret = []byte("gu123")

//签发人
const AppIss = "guwen"

// GenToken 生成JWT
func GenToken(id int,username string) (string, error) {
	u := &MyUser{
		Id:       id,
		Username: username,
	}
	//JWT默认结构体
	stdClaims := jwt.StandardClaims{
		ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(),
		IssuedAt:  time.Now().Unix(),
		Id:        fmt.Sprintf("%d", id),
		Issuer:    AppIss,
	}
	//植入的结构体
	c := MyClaims{
		User:           u,
		StandardClaims: stdClaims,
	}
	// 使用指定的签名方法创建签名对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
	// 使用指定的secret签名并获得完整的编码后的字符串token
	return token.SignedString(MySecret)
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (*MyClaims, error) {
	// 解析token
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return MySecret, nil
	})
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid { // 校验token
		return claims, nil
	}
	return nil, fmt.Errorf("无效的token")
}

// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware(r *ghttp.Request) {
	//从请求头获取数据
	restore := r.GetHeader("Token")
	if restore == "" {
		r.Response.WriteJson(AdminHandlerResponse{
			Status: 0,
			Error:  "无法获取请求头中的token",
			Data:   nil,
		})
		return
	}
	resMyClaims, err3 := ParseToken(restore)
	if err3 != nil {
		r.Response.WriteJson(AdminHandlerResponse{
			Status: 0,
			Error:  "JWT解析发生错误,错误原因:" + err3.Error(),
			Data:   nil,
		})
		return
	}
	if resMyClaims == nil {
		r.Response.WriteJson(AdminHandlerResponse{
			Status: 0,
			Error:  "无法解析用户数据",
			Data:   nil,
		})
		return
	}
	/************将JWT中解析出来的用户模型放到   CTX上下文对象中***************************************/
	r.SetCtxVar("MyUser", resMyClaims.User)
	if resMyClaims.Issuer != AppIss {
		r.Response.WriteJson(AdminHandlerResponse{
			Status: 0,
			Error:  "发行者错误",
			Data:   nil,
		})
		return
	}
	//超时
	if resMyClaims.ExpiresAt < gtime.Timestamp() {
		r.Response.WriteJson(AdminHandlerResponse{
			Status: 0,
			Error:  "token过期",
			Data:   nil,
		})
		return
	}
	//没有问题继续
	r.Middleware.Next()
}

